﻿using System;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Json;
using System.Text;
using Xoohoo.Infrastructure.FastReflectionLib;
using System.ComponentModel;
using System.Linq.Expressions;

namespace Xoohoo.Extensions
{
    public static class ObjectExtensions
    {
        public static string ToJson(this object source)
        {
            string serializedObject = string.Empty;

            if (source != null)
            {
                DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(source.GetType());

                using (MemoryStream memStream = new MemoryStream())
                {
                    jsonSerializer.WriteObject(memStream, source);
                    memStream.Position = 0;
                    serializedObject = new StreamReader(memStream).ReadToEnd();
                }
            }

            return serializedObject;
        }

        public static object FromJson(this Type type, string serializedObject)
        {
            object filledObject = null;

            if (!string.IsNullOrEmpty(serializedObject))
            {
                DataContractJsonSerializer dcjs = new DataContractJsonSerializer(type);

                using (MemoryStream memStream = new MemoryStream())
                {
                    StreamWriter writer = new StreamWriter(memStream, Encoding.Default);
                    writer.Write(serializedObject);
                    writer.Flush();

                    memStream.Position = 0;

                    try
                    {
                        filledObject = dcjs.ReadObject(memStream);
                    }
                    catch (SerializationException)
                    {
                        filledObject = null;
                    }
                }
            }

            return filledObject;
        }

        /// <summary>
        /// 如果源对象为null，则返回null，否则返回其ToString方法返回值
        /// </summary>
        /// <param name="source">源对象</param>
        /// <returns>字符串</returns>
        public static string ToNullableString(this object source)
        {
            if (source == null)
                return null;
            else
                return source.ToString();
        }

        /// <summary>
        /// 深度克隆
        /// </summary>
        /// <param name="source">源对象</param>
        /// <returns>新对象</returns>
        public static object DeepClone(this object source)
        {
            if (source == null)
                return null;

            using (MemoryStream memStream = new MemoryStream())
            {
                BinaryFormatter binaryFormatter = new BinaryFormatter(null,
                     new StreamingContext(StreamingContextStates.Clone));
                binaryFormatter.Serialize(memStream, source);
                memStream.Position=0;
                return binaryFormatter.Deserialize(memStream);
            }
        }

        /// <summary>
        /// 创建一个新的类型的对象，并将现有对象的属性值赋给新对象相同名称的属性
        /// </summary>
        /// <typeparam name="T">新对象的类型</typeparam>
        /// <param name="source">现有对象</param>
        /// <returns>新的对象</returns>
        public static T ToModel<T>(this object source) where T : new()
        {
            if (source == null) return default(T);

            Type type = typeof(T);
            T target = new T();

            return (T)UpdateFrom(target, source);
        }

        /// <summary>
        /// 将源对象的属性值赋给目标对象相同名称的属性
        /// </summary>
        /// <typeparam name="T">目标类型</typeparam>
        /// <param name="target">目标对象</param>
        /// <param name="source">源对象</param>
        /// <returns>目标类型</returns>
        public static T UpdateFrom<T>(this T target, object source)
        {
            if (target == null) return default(T);
            if (source == null) return target;

            Type type = typeof(T);

            foreach (PropertyDescriptor sourcePropertyDescriptor in TypeDescriptor.GetProperties(source))
            {
                PropertyInfo targetPropertyInfo = type.GetProperty(sourcePropertyDescriptor.Name, BindingFlags.Instance | BindingFlags.Public);
                if (targetPropertyInfo != null && targetPropertyInfo.CanWrite)
                {
                    var targetPropertyAccessor = new PropertyAccessor(targetPropertyInfo);
                    if (targetPropertyInfo.PropertyType.IsEnum)
                        targetPropertyAccessor.SetValue(target, Enum.ToObject(targetPropertyInfo.PropertyType, sourcePropertyDescriptor.GetValue(source)));
                    else
                        targetPropertyAccessor.SetValue(target, sourcePropertyDescriptor.GetValue(source));
                }
            }
            return target;
        }

    }
}
